package jp.co.nes.awssupport.compile.function;

import java.io.IOException;
import java.time.YearMonth;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jp.co.nes.awssupport.business.base.AbstractRequestWithCompletionStateHandler;
import jp.co.nes.awssupport.business.bean.BillingCsvBean;
import jp.co.nes.awssupport.business.bean.CommonContainerBean.ProcessingResult;
import jp.co.nes.awssupport.business.bean.CompileContainerBean;
import jp.co.nes.awssupport.business.bean.CompileInfoBean;
import jp.co.nes.awssupport.business.bean.ManifestBean;
import jp.co.nes.awssupport.business.logic.BillingCsvConvertor;
import jp.co.nes.awssupport.business.logic.BillingCsvReader;
import jp.co.nes.awssupport.business.logic.BlendedReservedManageProcessing;
import jp.co.nes.awssupport.business.logic.CompileProgressManage;
import jp.co.nes.awssupport.business.utility.BillingCsvUtil;
import jp.co.nes.awssupport.business.utility.ManifestHelper;
import jp.co.nes.awssupport.common.base.CommonException;
import jp.co.nes.awssupport.common.utility.AppInfoUtil;
import jp.co.nes.awssupport.common.utility.BeanUtil;
import jp.co.nes.awssupport.common.utility.DateUtil;

/**
 * 融通リザーブド管理出力です(Lambda)。
 */
public class BlendedReservedManage
        extends AbstractRequestWithCompletionStateHandler<CompileInfoBean, CompileContainerBean<CompileInfoBean>> {

    /** ロガー */
    private static Logger logger = LoggerFactory.getLogger(BlendedReservedManage.class);

    /**
     * デフォルトコンストラクタです。
     */
    public BlendedReservedManage() {
    }

    /*
     * (非 Javadoc)
     * @see jp.co.nes.awssupport.common.base.AbstractRequestHandler#handleRequest(java.lang.Object)
     */
    @Override
    protected CompileContainerBean<CompileInfoBean> handleRequest(CompileInfoBean input) {
        String targetYearMonthValue = input.getTargetYearMonthValue();
        YearMonth targetYearMonth = DateUtil.toYearMonth(targetYearMonthValue);
        CompileProgressManage cpManage = new CompileProgressManage(targetYearMonth);
        if (cpManage.isConfirmedBillingCsv()) {
            String categoryCsv = "BillingCsv";
            String bucketName = AppInfoUtil.getValAndEmptyChk(categoryCsv, "BucketName");
            String objectKey = AppInfoUtil.getValAndEmptyChk(categoryCsv, "ObjectKey");
            String manifestObjectKey = AppInfoUtil.getValAndEmptyChk(categoryCsv, "ManifestObjectKey");

            String category = "BlendedReservedManage";
            int max = AppInfoUtil.getIntValAndMinChk(category, "BatchExecuteMaxCount", 5000, 1);

            if (logger.isDebugEnabled()) {
                logger.debug(categoryCsv + ",BucketName=" + bucketName);
                logger.debug(categoryCsv + ",ObjectKey(before formatting)=" + objectKey);
                logger.debug(categoryCsv + ",ManifestObjectKey(before formatting)=" + manifestObjectKey);
                logger.debug(category + ",BatchExecuteMaxCount=" + max);
            }

            String linkedAccountId = input.getLinkedAccountId();
            List<String> payerAccountIdList = getPayerAccountId(bucketName, targetYearMonth, linkedAccountId);
            if (payerAccountIdList.isEmpty()) {
                // 通常発生しない。
                throw new CommonException("payerAccountId is null or empty");
            }

            List<String> convertNullResultList = new ArrayList<>();
            BillingCsvConvertor bcConvertor = new BillingCsvConvertor(targetYearMonth);

            for (String payerAccountId : payerAccountIdList) {
                List<BillingCsvBean> csvList = new ArrayList<>();

                String oKey = String.format(objectKey, targetYearMonthValue, payerAccountId, linkedAccountId);
                String moKey = String.format(manifestObjectKey, targetYearMonthValue, payerAccountId);

                ManifestBean manifestBean = getManifestBean(bucketName, moKey);
                try (BillingCsvReader reader = createBillingCsvReader(manifestBean, bucketName, oKey)) {

                    while (reader.readBean()) {
                        if (reader.isHeader()) {
                            // ファイルの1行目の場合
                            continue;
                        }

                        if (reader.isInvalidLine()) {
                            // 分割後のファイルを読み込んでいるので、通常は発生しない。
                            throw new CommonException("Read invalid line");
                        }

                        BillingCsvBean csvBean = reader.getBean();
                        if (!BillingCsvUtil.isBlendedReserved(csvBean)) {
                            // 融通リザーブド管理を登録する必要がない場合
                            continue;
                        }

                        BillingCsvBean convertCsvBean = bcConvertor.convertUsageType(csvBean);
                        if (convertCsvBean == null) {
                            // 融通リザーブド管理の場合、2018/02/20時点では、nullで返却されるものは存在しないが、
                            // 将来、nullで返却されるものが発生した際に備えて、null判断を実施している。
                            StringBuilder builder = new StringBuilder();
                            builder.append(csvBean.getLineItemType());
                            builder.append(":");
                            builder.append(csvBean.getLinkedAccountId());
                            builder.append(":");
                            builder.append(csvBean.getProductName());
                            builder.append(":");
                            builder.append(csvBean.getUsageType());
                            String addValue = builder.toString();
                            if (!convertNullResultList.stream().anyMatch(value -> value.equals(addValue))) {
                                convertNullResultList.add(addValue);
                            }
                        } else {
                            csvList.add(convertCsvBean);
                        }
                    }
                } catch (IOException e) {
                    throw new CommonException(e);
                }

                BlendedReservedManageProcessing brmProcessing = createBlendedReservedManageProcessing(bucketName, oKey);
                brmProcessing.save(csvList, max);
            }

            loggingConvertNullResultList(convertNullResultList);
        }

        // 返却内容の設定
        CompileInfoBean info = new CompileInfoBean();
        BeanUtil.copyProperties(info, input);
        CompileContainerBean<CompileInfoBean> output = new CompileContainerBean<>();
        output.setInfoBean(info);
        output.setProcessingResult(ProcessingResult.SUCCESS);
        return output;
    }

    /**
     * 一括請求アカウントIDを返却します。
     *
     * @param bucketName バケット名
     * @param targetYearMonth 処理対象年月
     * @param linkedAccountId 連結アカウントID
     * @return 一括請求アカウントIDリスト
     */
    protected List<String> getPayerAccountId(String bucketName, YearMonth targetYearMonth, String linkedAccountId) {
        return BillingCsvReader.getPayerAccountId(bucketName, targetYearMonth, linkedAccountId);
    }

    /**
     * 請求CSVのマニュフェストを返却します。
     *
     * @param bucketName バケット名
     * @param objectKey オブジェクトキー
     * @return 請求CSVのマニュフェスト
     */
    protected ManifestBean getManifestBean(String bucketName, String objectKey) {
        ManifestHelper manifestHelper = new ManifestHelper(bucketName, objectKey);
        return manifestHelper.getManifestBean();
    }

    /**
     * 請求CSVReaderを生成します。
     *
     * @param manifestBean マニュフェスト
     * @param bucketName バケット名
     * @param objectKey オブジェクトキー
     * @return 請求CSVReader
     */
    protected BillingCsvReader createBillingCsvReader(ManifestBean manifestBean, String bucketName, String objectKey) {
        return new BillingCsvReader(manifestBean, bucketName, objectKey);
    }

    /**
     * 融通リザーブド管理処理を生成します。
     *
     * @param bucketName バケット名
     * @param objectKey オブジェクトキー
     * @return 融通リザーブド管理処理
     */
    protected BlendedReservedManageProcessing createBlendedReservedManageProcessing(String bucketName,
            String objectKey) {
        return new BlendedReservedManageProcessing(bucketName, objectKey);
    }

    /**
     * 請求CSV変換でnullが返却されたものについてログ出力します。
     *
     * @param convertNullResultList 請求CSV変換でnullが返却されたリスト
     */
    private void loggingConvertNullResultList(List<String> convertNullResultList) {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < convertNullResultList.size(); i++) {
            if (i == 0) {
                builder.append("Convert Null Result Info [lineItemType:linkedAccountId:productName:usageType=");
            }
            builder.append(convertNullResultList.get(i));
            if (i == convertNullResultList.size() - 1) {
                builder.append("]");
            } else {
                builder.append(",");
            }
        }
        String value = builder.toString();
        if (!StringUtils.isEmpty(value)) {
            logger.warn(builder.toString());
        }
    }
}
